let hiddenTextarea: HTMLTextAreaElement | undefined;

const HIDDEN_STYLE = `
  height:0 !important;
  visibility:hidden !important;
  overflow:hidden !important;
  position:absolute !important;
  z-index:-1000 !important;
  top:0 !important;
  right:0 !important
`;

const CONTEXT_STYLE = [
    "letter-spacing",
    "line-height",
    "padding-top",
    "padding-bottom",
    "font-family",
    "font-weight",
    "font-size",
    "text-rendering",
    "text-transform",
    "width",
    "text-indent",
    "padding-left",
    "padding-right",
    "border-width",
    "box-sizing"
];

function calculateNodeStyling(targetElement: HTMLTextAreaElement) {
    const style = window.getComputedStyle(targetElement);

    const boxSizing = style.getPropertyValue("box-sizing");

    const paddingSize =
        parseFloat(style.getPropertyValue("padding-bottom")) + parseFloat(style.getPropertyValue("padding-top"));

    const borderSize =
        parseFloat(style.getPropertyValue("border-bottom-width")) +
        parseFloat(style.getPropertyValue("border-top-width"));

    const contextStyle = CONTEXT_STYLE.map((name) => `${name}:${style.getPropertyValue(name)}`).join(";");

    return { contextStyle, paddingSize, borderSize, boxSizing };
}

export function calcTextareaHeight(
    targetElement: HTMLTextAreaElement,
    minRows = 1,
    maxRows?: number
): { minHeight: string; height: string } {
    if (!hiddenTextarea) {
        hiddenTextarea = document.createElement("textarea");
        document.body.appendChild(hiddenTextarea);
    }

    let { paddingSize, borderSize, boxSizing, contextStyle } = calculateNodeStyling(targetElement);

    hiddenTextarea.setAttribute("style", `${contextStyle};${HIDDEN_STYLE}`);
    hiddenTextarea.value = targetElement.value || targetElement.placeholder || "";

    let height = hiddenTextarea.scrollHeight;
    const result = {
        height: "",
        minHeight: ""
    };

    if (boxSizing === "border-box") {
        height = height + borderSize;
    } else if (boxSizing === "content-box") {
        height = height - paddingSize;
    }

    hiddenTextarea.value = "";
    let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;

    if (minRows !== undefined) {
        let minHeight = singleRowHeight * minRows;
        if (boxSizing === "border-box") {
            minHeight = minHeight + paddingSize + borderSize;
        }
        height = Math.max(minHeight, height);
        result.minHeight = `${minHeight}px`;
    }
    if (maxRows !== undefined) {
        let maxHeight = singleRowHeight * maxRows;
        if (boxSizing === "border-box") {
            maxHeight = maxHeight + paddingSize + borderSize;
        }
        height = Math.min(maxHeight, height);
    }
    result.height = `${height}px`;
    hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);
    hiddenTextarea = undefined;
    return result;
}
